﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Design;
using System.ComponentModel;
using SharpGL;
using SharpGL.SceneGraph;
using SharpGL.SceneGraph.Core;
using System.Runtime.InteropServices;

namespace MegaTexture
{
    public class MegaTexture
    {
        public enum TTextureMode              { mDecal, tmModulate, tmBlend, tmReplace };
        public enum TMinFilter                { miNearest, miLinear, miNearestMipmapNearest, miLinearMipmapNearest, 
                                               miNearestMipmapLinear, miLinearMipmapLinear };
        public enum TMagFilter                 { maNearest, maLinear };
        public enum TTextureChange             { tcImage, tcParams };



        private uint                    m_iMaxTexSize; //макс размер текстуры, определяемый железом
        private Bitmap                  m_bmpImage; //загруженная в качестве текстуры картинка
        private OpenGL                  m_gl; //контекст устройства OpenGL
        private uint                    m_uiTexCellHeight; //высота куска текстуры
        private uint                    m_uiTexCellWidth; //ширина куска текстуры
        private uint                    m_uiTexRows; //на сколько строк побита текстура
        private uint                    m_uiTexCols; //на сколько столбцов побита текстура
        private List<uint>              m_lstTexCells; //массив хэндлов на куски текстуры
        private TTextureMode            m_tmTextureMode; //режим чего-то там связанного с текстурой
        private TMinFilter              m_mfMinFilter; //параметры фильтрации для уменьшения
        private TMagFilter              m_mfMagFilter; //параметры фильтрации для увеличения
        private HashSet<TTextureChange> m_tcChanges; //флаги изменений в текстуре
        private void                    SetMagFilter(TMagFilter mfValue)
        {
            if (mfValue != m_mfMagFilter)
            {
                m_mfMagFilter = mfValue; //установка параметра фильтра
                ReloadImage(); //перезагрузка
            }
        }//функции установки параметров фильтрации
        private void                    SetMinFilter(TMinFilter mfValue)
        {
            if (mfValue != m_mfMinFilter)
            {
                m_mfMinFilter = mfValue;
                ReloadImage();
            }
        }//для увеличения и уменьшения соответственно
        private void                    SetTexCellHeight(uint uiValue) //установка высоты текстуры
        {
            if (uiValue != m_uiTexCellHeight)
            {
                if (uiValue != 0)
                {
                    if (!IsPowerOf2((int)uiValue))
                    {
                        uiValue = RoundDownToPowerOf2(uiValue);
                    }
                }
                m_uiTexCellHeight = uiValue;
                m_tcChanges.Add(TTextureChange.tcImage);
            }
        }
        private void                    SetTexCellWidth(uint uiValue) //установка ширины текстуры
        {
            if (uiValue != m_uiTexCellWidth)
            {
                if (uiValue != 0)
                {
                    if (!IsPowerOf2((int)uiValue))
                    {
                        uiValue = RoundDownToPowerOf2(uiValue);
                    }
                }
                m_uiTexCellWidth = uiValue;
                m_tcChanges.Add(TTextureChange.tcImage);
            }
        }
        private void                    SetTextureMode(TTextureMode tmValue)//установка режима
        {
            if (tmValue != m_tmTextureMode)
            {
                m_tmTextureMode = tmValue;
                m_tcChanges.Add(TTextureChange.tcParams);
            }
        }          
              

        protected void          Handle_Changes()
        {
            uint uiTexCellHeight;
            uint uiTexCellWidth;
            if ((m_lstTexCells.Count == 0) && (!m_tcChanges.Contains(TTextureChange.tcImage)))
            {
                m_tcChanges.Add(TTextureChange.tcImage);
            }

            if (m_tcChanges.Count != 0)
            {
                try 
                {
                    if (m_tcChanges.Contains(TTextureChange.tcImage))
                    {
                        uiTexCellHeight = m_uiTexCellHeight;
                        uiTexCellWidth = m_uiTexCellWidth;
                        if ((uiTexCellHeight == 0) || (uiTexCellWidth == 0))
                        {
                            if (m_iMaxTexSize == 0)
                            {
                                int[] textureMaxSize = { 0 };
                                m_gl.GetInteger(OpenGL.GL_MAX_TEXTURE_SIZE, textureMaxSize);
                                m_iMaxTexSize = (uint)textureMaxSize[0];                                
                            }
                            if ((uiTexCellHeight == 0) || (uiTexCellHeight > m_iMaxTexSize))
                            {
                                uiTexCellHeight = m_iMaxTexSize;
                            }
                            if ((uiTexCellWidth == 0) || (uiTexCellWidth > m_iMaxTexSize))
                            {
                                uiTexCellWidth = m_iMaxTexSize;
                            }
                        }
                        DestroyHandles();                        
                        InitTexCells(m_bmpImage, uiTexCellHeight, uiTexCellWidth, false, ref m_uiTexRows, ref m_uiTexCols, ref m_lstTexCells); 
                        if (!m_tcChanges.Contains(TTextureChange.tcParams))
                        {
                            m_tcChanges.Add(TTextureChange.tcParams);
                        }                    
                    }
                    if (m_tcChanges.Contains(TTextureChange.tcParams))
                    {
                        ApplyParamsForTexCells(m_tmTextureMode, m_mfMinFilter, m_mfMagFilter, m_lstTexCells);
                    }

                }
                finally
                {
                    m_tcChanges.Clear();
                }
            }
        }
        protected Boolean       IsPowerOf2(int iValue)
        {
            return ((iValue != 0) && !Convert.ToBoolean(iValue & (iValue - 1)));
        }
        protected uint          RoundDownToPowerOf2(uint iValue)
        {
            int pow = 0;//считаем количество значащих разрядов
            while (iValue > 0)
            {
                iValue <<= 1;
                pow++;
            }
            return Convert.ToUInt32(Math.Pow(2, --pow));
        }
        protected uint          RoundUpToPowerOf2(uint iValue)
        {
            int pow = 0;//считаем количество значащих разрядов
            while (iValue > 0)
            {
                iValue <<= 1;
                pow++;
            }
            return Convert.ToUInt32(Math.Pow(2, pow));
        }
        protected Vertex        ComputeNormal(Vertex vA, Vertex vB, Vertex vC)
        {
            Vertex vNormal;
            vNormal.X = (vA.Y - vB.Y) * (vA.Z + vB.Z) +
                        (vB.Y - vC.Y) * (vB.Z + vC.Z) +
                        (vC.Y - vA.Y) * (vC.Z + vA.Z);
            vNormal.Y = (vA.Z - vB.Z) * (vA.X + vB.X) +
                        (vB.Z - vC.Z) * (vB.X + vC.X) +
                        (vC.Z - vA.Z) * (vC.X + vA.X);
            vNormal.Z = (vA.X - vB.X) * (vA.Y + vB.Y) +
                        (vB.X - vC.X) * (vB.Y + vC.Y) +
                        (vC.X - vA.X) * (vC.Y + vA.Y);
            return vNormal;                
        }
        protected float         ComputeStep(float fBegin, float fEnd, float fLenCoeff, uint uiStepsCount)
        {
            float fk;
            if (uiStepsCount != 0)
            {
                if (fEnd != 0.0F)
                {
                    fk = (fLenCoeff * (fEnd - fBegin) + fBegin) / fEnd;
                    return (fk * fEnd - fBegin) / uiStepsCount;
                }
                else if (fBegin != 0.0F)
                {
                    return -fLenCoeff * fBegin / uiStepsCount;
                }
                else
                {
                    return 0.0F;
                }
            }
            else
            {
                return 0.0F;
            }
        }
        protected Vertex        ComputeSteps(Vertex vA, Vertex vB, float fLenCoeff, uint uiStepsCount)
        {
            Vertex vSteps;
            vSteps.X = ComputeStep(vA.X, vB.X, fLenCoeff, uiStepsCount);
            vSteps.Y = ComputeStep(vA.Y, vB.Y, fLenCoeff, uiStepsCount);
            vSteps.Z = ComputeStep(vA.Z, vB.Z, fLenCoeff, uiStepsCount);
            return vSteps;
        }
        protected void          ApplyParams(TTextureMode tmTextureMode, TMinFilter mfMinFilter, TMagFilter mfMagFilter)
        {
            m_gl.Hint(OpenGL.GL_PERSPECTIVE_CORRECTION_HINT, OpenGL.GL_NICEST);
            m_gl.PixelStore(OpenGL.GL_UNPACK_ALIGNMENT,   1);
            m_gl.PixelStore(OpenGL.GL_UNPACK_ROW_LENGTH,  0);
            m_gl.PixelStore(OpenGL.GL_UNPACK_SKIP_ROWS,   0);
            m_gl.PixelStore(OpenGL.GL_UNPACK_SKIP_PIXELS, 0);
            //-------
            m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_S, OpenGL.GL_CLAMP);
            m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_T, OpenGL.GL_CLAMP);
            //-----------
            switch (mfMinFilter)
            {
                case TMinFilter.miNearest: 
                    m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_NEAREST);
                    break;
                case TMinFilter.miLinear:
                    m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR);
                    break;
                case TMinFilter.miNearestMipmapNearest:
                    m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_NEAREST_MIPMAP_NEAREST);
                    break;
                case TMinFilter.miLinearMipmapNearest:
                    m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR_MIPMAP_NEAREST);
                    break;
                case TMinFilter.miNearestMipmapLinear:
                    m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_NEAREST_MIPMAP_LINEAR);
                    break;
                case TMinFilter.miLinearMipmapLinear:
                    m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR_MIPMAP_LINEAR);
                    break;
            }
            switch (mfMagFilter)
            {
                case TMagFilter.maLinear:
                    m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR);
                    break;
                case TMagFilter.maNearest:
                    m_gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_NEAREST);
                    break;
            }
            switch (tmTextureMode)
            {
                case TTextureMode.mDecal:
                    m_gl.TexEnv(OpenGL.GL_TEXTURE_ENV, OpenGL.GL_TEXTURE_ENV_MODE, OpenGL.GL_DECAL);
                    break;
                case TTextureMode.tmBlend:
                    m_gl.TexEnv(OpenGL.GL_TEXTURE_ENV, OpenGL.GL_TEXTURE_ENV_MODE, OpenGL.GL_BLEND);
                    break;
                case TTextureMode.tmModulate:
                    m_gl.TexEnv(OpenGL.GL_TEXTURE_ENV, OpenGL.GL_TEXTURE_ENV_MODE, OpenGL.GL_MODULATE);
                    break;
                case TTextureMode.tmReplace:
                    m_gl.TexEnv(OpenGL.GL_TEXTURE_ENV, OpenGL.GL_TEXTURE_ENV_MODE, OpenGL.GL_REPLACE);
                    break;
            }
        }
        protected void          ApplyParamsForTexCells(TTextureMode tmTextureMode, TMinFilter mfMinFilter,
                                    TMagFilter mfMagFilter, List<uint> lstTexCells)
        {
            int iTexInd;
            uint uiTexID;
            for (iTexInd = 0; iTexInd < lstTexCells.Count - 1; iTexInd++)
            {
                uiTexID = lstTexCells.ElementAt(iTexInd);
                m_gl.BindTexture(OpenGL.GL_TEXTURE_2D, uiTexID);
                ApplyParams(tmTextureMode, mfMinFilter, mfMagFilter);
            }
        }
        protected void          InitTexCells(Bitmap hBitmap, uint uiTexCellHeight, uint uiTexCellWidth, Boolean bAsMipmaps,
                                    ref uint uiTexRows, ref uint uiTexCols, ref List<uint> lstTexCells)
        {
            if ((uiTexCellHeight == 0) || (uiTexCellWidth == 0))
            {
                uiTexRows = 0;
                uiTexCols = 0;
                lstTexCells.Clear();
                return;
            }
            uiTexCellHeight = RoundDownToPowerOf2(uiTexCellHeight);
            uiTexCellWidth = RoundDownToPowerOf2(uiTexCellWidth);
            uint uiCellImageSize = uiTexCellHeight * uiTexCellWidth;
            lstTexCells.Clear();
            uint[] glTextureArray = new uint[1] { 0 };

            for (int rowcounter = 0; rowcounter < uiTexRows; rowcounter++)
            {
                for (int colcounter = 0; colcounter < uiTexCols; colcounter++)
                {
                    BitmapData bmpdata = hBitmap.LockBits(new Rectangle(rowcounter * (int)uiTexCellWidth,
                                                                        colcounter * (int)uiTexCellHeight,
                                                                        (int)uiTexCellWidth,
                                                                        (int)uiTexCellHeight), 
                                                                        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                    m_gl.GenTextures(1, glTextureArray);
                    lstTexCells.Insert((rowcounter * (int)m_uiTexCols + colcounter), glTextureArray[0]);
                    m_gl.BindTexture(OpenGL.GL_TEXTURE_2D, glTextureArray[0]);
                    if (bAsMipmaps == false)
                    {
                        m_gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 
                                        0, 
                                        (int)OpenGL.GL_RGBA, 
                                        (int)uiTexCellWidth, 
                                        (int)uiTexCellHeight, 
                                        0, 
                                        OpenGL.GL_BGRA, 
                                        OpenGL.GL_UNSIGNED_BYTE, 
                                        bmpdata.Scan0);
                    }
                    else
                    {
                        m_gl.Build2DMipmaps(OpenGL.GL_TEXTURE_2D, 
                                            3, 
                                            (int)uiTexCellWidth, 
                                            (int)uiTexCellHeight, 
                                            OpenGL.GL_BGRA, 
                                            OpenGL.GL_UNSIGNED_BYTE, 
                                            bmpdata.Scan0);
                    }
                    hBitmap.UnlockBits(bmpdata);
                }
            }


        }
//        protected void          InternalRenderToUnitRect(uint uiFullHeight, uint uiFullWidth, uint uiCellHeight,uint uiCellWidth,
//                                    Boolean bGenNormals, List<uint> lstTexCells);
        protected void          InternalRenderToQuad(uint uiFullHeight, uint uiFullWidth, uint uiCellHeight, uint uiCellWidth,
                                    Boolean bGenNormals, Boolean bUseTriangles, Vertex vA, Vertex vB, Vertex vC, Vertex vD,
                                    List<uint> lstTexCells)
        {
            Vertex vA1;
            Vertex vB1;
            Vertex vC1;
            Vertex vD1;
            float fLengthAB;
            float fLenghtDC;
            float fLengthA1D1;
            float fLengthB1C1;
            float fTmpLength;
            float fHeightExCoeff;
            float fWidthExCoeff;
            Vertex vStepAB;
            Vertex vStepDC;
            Vertex vStepA1D1;
            Vertex vStepB1C1;
            float fExStepAB;
            float fExStepDC;
            float fExStepA1D1;
            float fExStepB1C1;
            Vertex vLeftTop;
            Vertex vLeftBottom;
            Vertex vRightTop;
            Vertex vRightBottom;
            Vertex vNormal;
            uint uiRowsCount;
            uint uiColsCount;
            uint uiRow, uiCol;
            uint uiRow1, uiCol1;
            int iMaxNumOfTexCells;
            float fRightTopS;
            float fRightBottomS;
            float fLeftBottomT;
            float fRightBottomT;
            int iTexInd;
            uint uiTexID;
            //==========================
            iMaxNumOfTexCells = lstTexCells.Count;
            if ((uiFullHeight == 0) || (uiFullWidth == 0) ||
                (uiCellHeight == 0) || (uiCellWidth == 0) ||
                (iMaxNumOfTexCells == 0))
            {
                return; //здесь нужно воткнуть что-то адекватное, возвращающее сообщение об ошибке
            }
            
            uiRowsCount = (uint)Math.Ceiling(Convert.ToDouble(uiFullHeight) / Convert.ToDouble(uiCellHeight));
            uiColsCount = (uint)Math.Ceiling(Convert.ToDouble(uiFullWidth) / Convert.ToDouble(uiCellWidth));

            fHeightExCoeff = Convert.ToSingle(uiRowsCount) * Convert.ToSingle(uiCellHeight) / Convert.ToSingle(uiFullHeight);
            fWidthExCoeff = Convert.ToSingle(uiColsCount) * Convert.ToSingle(uiCellWidth) / Convert.ToSingle(uiFullWidth);
            vStepAB = ComputeSteps(vA, vB, fHeightExCoeff, uiRowsCount);
            fLengthAB = (float)Math.Sqrt(Math.Pow(vA.X - vB.X, 2) +
                                    Math.Pow(vA.Y - vB.Y, 2) +
                                    Math.Pow(vA.Z - vB.Z, 2));
            fExStepAB = fLengthAB * fHeightExCoeff / Convert.ToSingle(uiRowsCount);
            vStepDC = ComputeSteps(vD, vC, fHeightExCoeff, uiRowsCount);
            fLenghtDC = (float)Math.Sqrt(Math.Pow(vD.X - vC.X, 2) +
                                    Math.Pow(vD.Y - vC.Y, 2) +
                                    Math.Pow(vD.Z - vC.Z, 2));
            fExStepDC = fLenghtDC * fHeightExCoeff / Convert.ToSingle(uiRowsCount);

            for (uiRow = 0; uiRow < uiRowsCount; uiRow++)
            {
                uiRow1 = uiRow + 1;
                
                vA1 = vA + (vStepAB * uiRow);

                vB1 = vA + (vStepAB * (uiRow1 + 1));

                vC1 = vD + (vStepDC * (uiRow + 1));

                vD1 = vD + (vStepDC * uiRow);
                                

                fTmpLength = (float)Math.Sqrt(Math.Pow(vA.X - vB1.X, 2) +
                                                Math.Pow(vA.Y - vB1.Y, 2) +
                                                Math.Pow(vA.Z - vB1.Z, 2));



                if (fTmpLength <= fLengthAB)
                {
                    fLeftBottomT = 1.0F;
                }
                else
                {
                    if (fExStepAB != 0)
                    {
                        fLeftBottomT = 1.0F - (fTmpLength - fLengthAB) / fExStepAB;
                    }
                    else
                    {
                        fLeftBottomT = 1.0F;
                    }
                    vB1 = vB;
                }
                fTmpLength = (float)Math.Sqrt(Math.Pow(vD.X - vC1.X, 2) +
                                                Math.Pow(vD.Y - vC1.Y, 2) +
                                                Math.Pow(vD.Z - vC1.Z, 2));
                if (fTmpLength <= fLenghtDC)
                {
                    fRightBottomT = 1.0F;
                }
                else
                {
                    if (fExStepDC != 0)
                    {
                        fRightBottomT = 1.0F - (fTmpLength - fLenghtDC) / fExStepDC;
                    }
                    else
                    {
                        fRightBottomT = 1.0F;
                    }
                vC1 = vC;
                }

                vStepA1D1 = ComputeSteps(vA1, vD1, fWidthExCoeff, uiColsCount);
                fLengthA1D1 = (float)Math.Sqrt(Math.Pow(vA1.X - vD1.X, 2) +
                                    Math.Pow(vA1.Y - vD1.Y, 2) +
                                    Math.Pow(vA1.Z - vD1.Z, 2));
                fExStepA1D1 = fLengthA1D1 * fWidthExCoeff / uiColsCount;
                vStepB1C1 = ComputeSteps(vB1, vC1, fWidthExCoeff, uiColsCount);
                fLengthB1C1 = (float)Math.Sqrt(Math.Pow(vB1.X - vC1.X, 2) +
                                    Math.Pow(vB1.Y - vC1.Y, 2) +
                                    Math.Pow(vB1.Z - vC1.Z, 2));
                fExStepB1C1 = fLengthB1C1 * fWidthExCoeff / uiColsCount;
                
                for (uiCol = 0; uiCol < uiColsCount; uiCol++)
                {
                    uiCol1 = uiCol + 1;

                    vLeftTop = vA1 + (vStepA1D1 * uiCol);

                    vRightTop = vA1 + (vStepA1D1 * (uiCol + 1));

                    vLeftBottom = vB1 + (vStepB1C1 * uiCol);

                    vRightBottom = vB1 + (vStepB1C1 * (uiCol + 1));
                                        
                    fTmpLength = (float)Math.Sqrt(Math.Pow(vA1.X - vRightTop.X, 2) +
                                                    Math.Pow(vA1.Y - vRightTop.Y, 2) +
                                                    Math.Pow(vA1.Z - vRightTop.Z, 2));
                    if (fTmpLength <= fLengthA1D1)
                    {
                        fRightTopS = 1.0F;
                    }
                    else
                    {
                        if (fExStepA1D1 != 0)
                        {
                            fRightTopS = 1.0F - (fTmpLength - fLengthA1D1) / fExStepA1D1;
                        }
                        else
                        {
                            fRightTopS = 1.0F;
                        }
                    vRightTop = vD1;

                    }
                    fTmpLength = (float)Math.Sqrt(Math.Pow(vB1.X - vRightBottom.X, 2) +
                                                    Math.Pow(vB1.Y - vRightBottom.Y, 2) +
                                                    Math.Pow(vB1.Z - vRightBottom.Z, 2));
                    if (fTmpLength <= fLengthB1C1)
                    {
                        fRightBottomS = 1.0F;
                    }
                    else
                    {
                        if (fExStepA1D1 != 0)
                        {
                            fRightBottomS = 1.0F - (fTmpLength - fLengthB1C1) / fExStepA1D1;
                        }
                        else
                        {
                            fRightBottomS = 1.0F;
                        }
                    vRightBottom = vC1;

                    }
                    iTexInd = (int)(uiRow * uiColsCount + uiCol);
                    if (iTexInd >= iMaxNumOfTexCells)
                    {
                        break;
                    }
                    uiTexID = lstTexCells.ElementAt(iTexInd);
                    m_gl.BindTexture(OpenGL.GL_TEXTURE_2D, uiTexID);
                    if (!bUseTriangles)
                    {
                        m_gl.Begin(OpenGL.GL_QUADS);
                        if (bGenNormals)
                        {
                            vNormal = ComputeNormal(vLeftTop, vLeftBottom, vRightBottom);
                            vNormal.Normalize();                            
                            m_gl.Normal(vNormal.X, vNormal.Y, vNormal.Z);
                        }
                        //left-bottom
                        m_gl.TexCoord(0.0, fLeftBottomT);
                        m_gl.Vertex(vLeftBottom.X, vLeftBottom.Y, vLeftBottom.Z);
                        //right-bottom
                        m_gl.TexCoord(fRightBottomS, fRightBottomT);
                        m_gl.Vertex(vRightBottom.X, vRightBottom.Y, vRightBottom.Z);
                        //right-top
                        m_gl.TexCoord(fRightTopS, 0.0);
                        m_gl.Vertex(vRightTop.X, vRightTop.Y, vRightTop.Z);
                        //left-top
                        m_gl.TexCoord(0.0, 0.0);
                        m_gl.Vertex(vLeftTop.X, vLeftTop.Y, vLeftTop.Z);
                        m_gl.End();
                    }
                    else
                    {
                        m_gl.Begin(OpenGL.GL_TRIANGLES);
                        if (bGenNormals)
                        {
                            vNormal = ComputeNormal(vLeftTop, vLeftBottom, vRightBottom);
                            vNormal.Normalize(); 
                            m_gl.Normal(vNormal.X, vNormal.Y, vNormal.Z);
                        }
                        //left-top
                        m_gl.TexCoord(0.0, 0.0);
                        m_gl.Vertex(vLeftTop.X, vLeftTop.Y, vLeftTop.Z);
                        //left-bottom
                        m_gl.TexCoord(0.0, fLeftBottomT);
                        m_gl.Vertex(vLeftBottom.X, vLeftBottom.Y, vLeftBottom.Z);
                        //right-bottom
                        m_gl.TexCoord(fRightBottomS, fRightBottomT);
                        m_gl.Vertex(vRightBottom.X, vRightBottom.Y, vRightBottom.Z);

                        if (bGenNormals)
                        {
                            vNormal = ComputeNormal(vLeftTop, vLeftBottom, vRightBottom);
                            vNormal.Normalize(); 
                            m_gl.Normal(vNormal.X, vNormal.Y, vNormal.Z);
                        }
                        //left-top
                        m_gl.TexCoord(0.0, 0.0);
                        m_gl.Vertex(vLeftTop.X, vLeftTop.Y, vLeftTop.Z);
                        //right-bottom
                        m_gl.TexCoord(fRightBottomS, fRightBottomT);
                        m_gl.Vertex(vRightBottom.X, vRightBottom.Y, vRightBottom.Z);
                        //right-top
                        m_gl.TexCoord(fRightTopS, 0.0);
                        m_gl.Vertex(vRightTop.X, vRightTop.Y, vRightTop.Z);
                        m_gl.End();
                    }
                }
            }
        }

        public void             DestroyHandles()
        {
            if (m_lstTexCells.Count > 0)
            {
                m_gl.DeleteTextures(m_lstTexCells.Count, m_lstTexCells.ToArray<uint>());
            }
            m_lstTexCells.Clear();
            ReloadImage();
        }
        public void             ReloadImage() //перезагрузка текстуры 
        {
            m_tcChanges.Add(TTextureChange.tcImage);
            m_tcChanges.Add(TTextureChange.tcParams);
        }

//        public void             RenderToUnitRect(Boolean bGenNormals);
//        public void             RenderToRect(Vertex vCenter, Vertex vVectorAB, float fHeight, float fWidth, Boolean bGenNormals);
//        public void             RenderToPlg(Vertex vA, Vertex vB, Vertex vC, Boolean bGenNormals);
        public void             RenderToQuad(Vertex vA, Vertex vB, Vertex vC, Vertex vD, Boolean bGenNormals)
        {
            m_gl.Enable(OpenGL.GL_TEXTURE_2D);
            Handle_Changes();
            InternalRenderToQuad((uint)m_bmpImage.Height, (uint)m_bmpImage.Width, m_uiTexCellHeight, m_uiTexCellWidth, 
                bGenNormals, true, vA, vB, vC, vD, m_lstTexCells);                    
        }//натянуть на четырехугольник

        //------------Свойства-----------------------------------
        public uint             GetHandle(int Index) //получение хэндла куска текстуры из массива
        {            
            Handle_Changes();
            if ((Index >= 0) && (m_lstTexCells.Count >= Index))
                return m_lstTexCells[Index];
            else
            {
                return 0;
            }
        }
        public int              NumOfHandles //количество кусков 
        {
            get
            {
                return m_lstTexCells.Count;
            }
        } 
        public Bitmap           Image //картинка
        {
            get
            {
                return m_bmpImage;
            }
            set 
            {
                m_bmpImage = value;
                m_tcChanges.Add(TTextureChange.tcImage); //картинка изменилась - установили флаг
            }
        }
        public TMagFilter       MagFilter //
        {
            get
            {
                return m_mfMagFilter;
            }
            set
            {
                SetMagFilter(value);
            }

        }
        public TMinFilter       MinFilter
        {
            get
            {
                return m_mfMinFilter;
            }
            set
            {
                SetMinFilter(value);
            }

        }
        public uint             TexCellHeight
        {
            get
            {
                return m_uiTexCellHeight;
            }
            set
            {
                SetTexCellHeight(value);
            }
        }
        public uint             TexCellWidth
        {
            get
            {
                return m_uiTexCellWidth;
            }
            set
            {
                SetTexCellWidth(value);
            }
        }
        public TTextureMode     TextureMode
        {
            get
            {
                return m_tmTextureMode;
            }
            set
            {
                SetTextureMode(value);
            }
        }
        //------------Методы------------------------------------
        public MegaTexture()
        {
            m_iMaxTexSize       = 0;
            m_uiTexCellHeight   = 0;
            m_uiTexCellWidth    = 0;
            m_uiTexRows         = 0;
            m_uiTexCols         = 0;
            m_tmTextureMode     = TTextureMode.tmModulate;
            m_mfMinFilter       = TMinFilter.miNearest;
            m_mfMagFilter       = TMagFilter.maNearest;
            m_tcChanges         = new HashSet<TTextureChange>();
            m_lstTexCells       = new List<uint>();
        }

        public void BindToGL(OpenGL gl)
        {
            if (m_gl != gl)
            {
                DestroyHandles();
                ReloadImage();
                m_gl = gl;
            }
        }//привязка к контексту устройства
        


























    }

}
